package sf.database.listener;

import sf.spring.util.CollectionUtils;
import sf.tools.ArrayUtils;

import javax.persistence.EntityListeners;
import javax.persistence.PostLoad;
import javax.persistence.PostPersist;
import javax.persistence.PostRemove;
import javax.persistence.PostUpdate;
import javax.persistence.PrePersist;
import javax.persistence.PreRemove;
import javax.persistence.PreUpdate;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * EntityListener监听器处理
 * 注意:只对实体类的操作和返回生效
 * @see javax.persistence.EntityListeners
 */
public class EntityListenerManager {
    /**
     * Domain --> EntityListener class-->Context
     */
    static Map<Class<?>, Set<Class<?>>> entityListenerClassMap = new ConcurrentHashMap<>(4);

    /**
     * EntityListener class-->Context 此处设计, 支持监听器共享
     */
    static Map<Class<?>, EntityListenerContext> entityListenerContextMap = new ConcurrentHashMap<>(4);

    /**
     * 默认的监听器
     */
    static Set<EntityListenerContext> commonListenerContexts = new LinkedHashSet<>(2);

    /**
     * 扫码实体类,提取EntityListeners 注解
     * @param entityClass
     */
    public static void scan(Class<?> entityClass) {
        EntityListeners entityListeners = entityClass.getAnnotation(EntityListeners.class);
        if (entityListeners != null && ArrayUtils.isNotEmpty(entityListeners.value())) {
            Class<?>[] classes = entityListeners.value();
            for (int i = 0; i < classes.length; i++) {
                Class<?> entityListenerClass = classes[i];
                Set<Class<?>> subSet = entityListenerClassMap.computeIfAbsent(entityClass, k -> new HashSet<>(2));
                subSet.add(entityListenerClass);
                if (!entityListenerContextMap.containsKey(entityListenerClass)) {
                    EntityListenerContext context = annotationMethod(entityListenerClass, null);
                    entityListenerContextMap.put(entityListenerClass, context);
                }
            }
        }
    }

    /**
     * 解析监听器中的各个监听接口
     * @param entityListenerClass
     * @param entityListenerObject
     * @return
     */
    private static EntityListenerContext annotationMethod(Class<?> entityListenerClass, Object entityListenerObject) {
        EntityListenerContext context = new EntityListenerContext();
        context.entityListener = entityListenerObject;
        context.entityListenerClass = entityListenerClass;

        if (!EntityListener.class.isAssignableFrom(entityListenerClass)) {
            Map<Class<?>, Method> map = context.map;
            Method[] methods = entityListenerClass.getDeclaredMethods();
            for (Method method : methods) {
                PrePersist prePersist = method.getAnnotation(PrePersist.class);
                PostPersist postPersist = method.getAnnotation(PostPersist.class);
                PostLoad postLoad = method.getAnnotation(PostLoad.class);
                PreUpdate preUpdate = method.getAnnotation(PreUpdate.class);
                PostUpdate postUpdate = method.getAnnotation(PostUpdate.class);
                PreRemove preRemove = method.getAnnotation(PreRemove.class);
                PostRemove postRemove = method.getAnnotation(PostRemove.class);
                if (prePersist != null) {
                    map.put(PrePersist.class, method);
                }
                if (postPersist != null) {
                    map.put(PostPersist.class, method);
                }
                if (postLoad != null) {
                    map.put(PostLoad.class, method);
                }
                if (preUpdate != null) {
                    map.put(PreUpdate.class, method);
                }
                if (postUpdate != null) {
                    map.put(PostUpdate.class, method);
                }
                if (preRemove != null) {
                    map.put(PreRemove.class, method);
                }
                if (postRemove != null) {
                    map.put(PostRemove.class, method);
                }
            }
        }
        return context;
    }

    public static void runPrePersist(Object object) {
        run(object, PrePersist.class);
    }

    public static void runPostPersist(Object object) {
        run(object, PostPersist.class);
    }

    public static void runPreUpdate(Object object) {
        run(object, PreUpdate.class);
    }

    public static void runPostUpdate(Object object) {
        run(object, PostUpdate.class);
    }

    public static void runPreRemove(Object object) {
        run(object, PreRemove.class);
    }

    public static void runPostRemove(Object object) {
        run(object, PostRemove.class);
    }

    public static void runPostLoad(Object object) {
        run(object, PostLoad.class);
    }

    private static void run(Object object, Class<?> annotationClazz) {
        if (object == null) {
            return;
        }
        Set<Class<?>> set = entityListenerClassMap.get(object.getClass());
        if (CollectionUtils.isNotEmpty(set)) {
            for (Class<?> clazz : set) {
                EntityListenerContext context = entityListenerContextMap.get(clazz);
                if (context != null) {
                    context.runEntityListener(object, annotationClazz);
                }
            }
        }
        if (CollectionUtils.isNotEmpty(commonListenerContexts)) {
            for (EntityListenerContext context : commonListenerContexts) {
                context.runEntityListener(object, annotationClazz);
            }
        }
    }

    /**
     * 添加通用监听器  可以为自定义监听器,也可以为EntityListener 的接口实现
     * @param contexts
     */
    public static void addCommonEntityListener(Object... contexts) {
        if (ArrayUtils.isNotEmpty(contexts)) {
            for (Object object : contexts) {
                EntityListenerContext context = annotationMethod(object.getClass(), object);
                context.entityListener = object;
                commonListenerContexts.add(context);
            }
        }
    }

    /**
     * 添加实体类的监听器
     * @param entityClass
     * @param entityListener
     */
    public static void addEntityListener(Class<?> entityClass, Object entityListener) {
        if (entityClass != null && entityListener != null) {
            Set<Class<?>> set = entityListenerClassMap.computeIfAbsent(entityClass, k -> new HashSet<>(2));
            set.add(entityListener.getClass());
            EntityListenerContext context = annotationMethod(entityListener.getClass(), entityListener);
            entityListenerContextMap.put(entityListener.getClass(), context);
        }
    }

    public static Set<EntityListenerContext> getCommonListenerContexts() {
        return commonListenerContexts;
    }

    public static Map<Class<?>, Set<Class<?>>> getEntityListenerClassMap() {
        return entityListenerClassMap;
    }

    public static Map<Class<?>, EntityListenerContext> getEntityListenerContextMap() {
        return entityListenerContextMap;
    }
}
